﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Hybrid.Threading.Sync
{
    class AnotherHybridLock:IDisposable
    {
        //由基元用户模式构造使用
        private int m_waiter = 0;
        //由基元内核模式构造使用
        private AutoResetEvent m_waiterLock = new AutoResetEvent(false);
        //控制自旋,希望提高性能
        private int m_spincount = 4000;//任意选择的一个计数
        //这些字段指出哪个线程拥有锁,以及拥有了它多少次
        private int m_owingThreadId = 0;
        private int m_recursion = 0;

        public void Enter()
        {
            //如果调用线程已经拥有锁,递增递归计算并返回
            int threadId = Thread.CurrentThread.ManagedThreadId;
            if (threadId == m_owingThreadId)
            {
                m_recursion++; return;
            }

            //调用线程不拥有锁,就尝试获取它
            SpinWait spinwait = new SpinWait();
            for (int spincount = 0; spincount < m_spincount; spincount++)
            {
                //如果锁可以自由使用,这个线程就获取他,设置一些状态并返回
                if (Interlocked.CompareExchange(ref m_waiter, 1, 0) == 0)
                {
                    //一个线程获得锁,我们记录它的ID,并指出线程拥有锁一次
                    m_owingThreadId = threadId;
                    m_recursion = 1;
                    return;
                }
                //给其它线程运行的机会,希望锁会被释放
                spinwait.SpinOnce();
            }

            //自旋结束,锁仍然没有获得,再试一次
            if (Interlocked.Increment(ref m_waiter) > 1)
            {
                //其它线程被阻塞,这个线程也必须阻塞
                m_waiterLock.WaitOne();//等待锁,性能损失
                //等这个线程醒来时,它拥有锁,设置一些状态并返回
            }
        }

        public void Leave()
        {
            //如果调用线程不拥有锁,表明存在一个BUG
            int threadId = Thread.CurrentThread.ManagedThreadId;
            if (threadId != m_owingThreadId)
                throw new SynchronizationLockException("Lock not owned by called thread.");

            //递减递归计数,如果这个线程仍然拥有锁,那么直接返回
            if (--m_recursion > 0) { return; }

            //如果没有其它线程被阻塞,直接返回
            if (Interlocked.Decrement(ref m_waiter) == 0)
            {
                return;
            }

            //有其它线程被阻塞,唤醒其中1个
            m_waiterLock.Set();//性能损失
        }



        #region IDisposable 成员

        public void Dispose()
        {
            m_waiterLock.Dispose();
        }

        #endregion
    }
}
